#ifndef MW_MESSAGE_H
#define MW_MESSAGE_H


/** Meanwhile */


#include <glib.h>
#include "mw-common.h"


/** @file mw-message.h
    @since 2.0.0

    Common messages and associated read/write functions */


G_BEGIN_DECLS


typedef enum mw_message_type {
  MW_MESSAGE_TYPE_HANDSHAKE = 0x0000,
  MW_MESSAGE_TYPE_HANDSHAKE_ACK = 0x8000,
  MW_MESSAGE_TYPE_LOGIN = 0x0001,
  MW_MESSAGE_TYPE_LOGIN_REDIRECT = 0x0018,
  MW_MESSAGE_TYPE_LOGIN_FORCE = 0x0016,
  MW_MESSAGE_TYPE_LOGIN_ACK = 0x8001,

  MW_MESSAGE_TYPE_CHANNEL_CREATE = 0x0002,
  MW_MESSAGE_TYPE_CHANNEL_CLOSE = 0x0003,
  MW_MESSAGE_TYPE_CHANNEL_SEND = 0x0004,
  MW_MESSAGE_TYPE_CHANNEL_ACCEPT = 0x0006,

  MW_MESSAGE_TYPE_SINGLE = 0x0007,

  MW_MESSAGE_TYPE_STATUS = 0x0009,
  MW_MESSAGE_TYPE_PRIVACY = 0x000b,

  MW_MESSAGE_TYPE_SENSE_SERVICE = 0x0011,

  MW_MESSAGE_TYPE_ADMIN_ANNOUNCE = 0x0019,
  MW_MESSAGE_TYPE_USER_ANNOUNCE = 0x0022,

} MwMessageType;


GType mw_message_type_get_type();


#define MW_TYPE_MESSAGE_TYPE (mw_message_type_get_type())



typedef enum mw_message_option {
  MW_MESSAGE_OPTION_ENCRYPTED = 0x4000,
  MW_MESSAGE_OPTION_ATTRIBS = 0x8000,

} MwMessageOption;


GType mw_message_option_get_type();


#define MW_TYPE_MESSAGE_OPTION (mw_message_option_get_type())



typedef struct mw_message_header {
  guint16 type;
  guint16 options;
  guint32 channel;
  MwOpaque attribs;

} MwMessageHeader;


void mw_message_header_read(MwReadBuffer *readb, MwMessageHeader *msgh,
			    GError **err);


void mw_message_header_write(MwWriteBuffer *writeb,
			     const MwMessageHeader *msgh);



typedef MwMessageHeader MwMessage;


/** the header structure of a message */
#define MW_MESSAGE_GET_HEADER(msg)  ((MwMessageHeader *)(msg))


/** the type field of a message's header */
#define MW_MESSAGE_GET_TYPE(msg) (MW_MESSAGE_GET_HEADER(msg)->type)


/** returns msg if it matches the given type. returns NULL and issues
    a warning if it does not */
gpointer mw_message_type_check(MwMessage *msg, MwMessageType type);


/** Casts msg to type if it matches type_enum, otherwise returns NULL
    and emits a warning */
#define MW_MESSAGE_TYPE_CAST(msg, type, type_enum)	\
  ((type) mw_message_type_check(msg, type_enum))


/** needs to be the first entry of any message structure */
#define MW_MESSAGE_STRUCT_HEADER  MwMessageHeader mw_msg_header


/** allocates the appropriate message by the given type */
MwMessageHeader *mw_message_new(MwMessageType type);


/** Peeks at the type, then allocates the appropriate message instance
    into msg, and calls the read function for the type */
void mw_message_read(MwReadBuffer *mwrb, MwMessage **msg, GError **err);


/** Writes out the message using the appropriate write function
    determined by the message's type */
void mw_message_write(MwWriteBuffer *mwwb, const MwMessage *msg);


/** Checks the message type, then calls the appropriate free function */
void mw_message_free(MwMessage *msg);



typedef struct mw_message_handshake {
  MW_MESSAGE_STRUCT_HEADER;
  
  guint16 major;
  guint16 minor;
  guint32 srvrcalc_addr;
  guint16 client_type;
  guint32 localcalc_addr;
  guint16 unknown_a;
  guint32 unknown_b;
  gchar *local_host;

} MwMessageHandshake;


#define MW_MESSAGE_HANDSHAKE(msg)			\
  MW_MESSAGE_TYPE_CAST((msg), (MwMessageHandshake *),	\
		       MW_MESSAGE_TYPE_HANDSHAKE)


MwMessageHandshake *mw_message_handshake_new();


void mw_message_handshake_free(MwMessageHandshake *msg);


void mw_message_handshake_read(MwReadBuffer *mwrb,
			       MwMessageHandshake *msg,
			       GError **err);


void mw_message_handshake_write(MwWriteBuffer *mwwb,
				MwMessageHandshake *msg);


typedef struct mw_message_handshake_ack {
  MW_MESSAGE_STRUCT_HEADER;

  guint16 major;
  guint16 minor;
  guint32 srvrcalc_addr;
  guint32 magic;
  MwOpaque key;

} MwMessageHandshakeAck;


#define MW_MESSAGE_HANDSHAKE_ACK(msg)				\
  MW_MESSAGE_TYPE_CAST((msg), (MwMessageHandshakeAck *),	\
		       MW_MESSAGE_TYPE_HANDSHAKE_ACK)


MwMessageHandshakeAck *mw_message_handshake_ack_new();


void mw_message_handshake_ack_free(MwMessageHandshakeAck *msg);


void mw_message_handshake_ack_read(MwReadBuffer *mwrb,
				   MwMessageHandshakeAck *msg,
				   GError **err);


void mw_message_handshake_ack_write(MwWriteBuffer *mwwb,
				    MwMessageHandshakeAck *msg);


typedef struct mw_message_login {
  MW_MESSAGE_STRUCT_HEADER;

  guint16 client_type;
  gchar *name;
  guint16 auth_type;
  MwOpaque auth_data;
  
} MwMessageLogin;


#define MW_MESSAGE_LOGIN(msg)				\
  MW_MESSAGE_TYPE_CAST((msg), (MwMessageLogin *),	\
		       MW_MESSAGE_TYPE_LOGIN)


MwMessageLogin *mw_message_login_new();


void mw_message_login_free(MwMessageLogin *msg);


void mw_message_login_read(MwReadBuffer *mwrb,
			   MwMessageLogin *msg,
			   GError **err);


void mw_message_login_write(MwWriteBuffer *mwwb,
			    MwMessageLogin *msg);


typedef struct mw_message_login_redirect {
  MW_MESSAGE_STRUCT_HEADER;
  
  gchar *host;
  gchar *server_id;

} MwMessageLoginRedirect;


#define MW_MESSAGE_LOGIN_REDIRECT(msg)				\
  MW_MESSAGE_TYPE_CAST((msg), (MwMessageLoginRedirect *),	\
		       MW_MESSAGE_TYPE_LOGIN_REDIRECT)


MwMessageLoginRedirect *mw_message_login_redirect_new();


void mw_message_login_redirect_free(MwMessageLoginRedirect *msg);


void mw_message_login_redirect_read(MwReadBuffer *mwrb,
				    MwMessageLoginRedirect *msg,
				    GError **err);


void mw_message_login_redirect_write(MwWriteBuffer *mwrb,
				     MwMessageLoginRedirect *msg);


typedef struct mw_message_login_force {
  MW_MESSAGE_STRUCT_HEADER;

} MwMessageLoginForce;


#define MW_MESSAGE_LOGIN_FORCE(msg)			\
  MW_MESSAGE_TYPE_CAST((msg), (MwMessageLoginForce *),	\
		       MW_MESSAGE_TYPE_LOGIN_FORCE)


MwMessageLoginForce *mw_message_login_force_new();


void mw_message_login_force_free(MwMessageLoginForce *msg);


void mw_message_login_force_read(MwReadBuffer *mwrb,
				 MwMessageLoginForce *msg,
				 GError *err);


void mw_message_login_force_write(MwWriteBuffer *mwwb,
				  MwMessageLoginForce *msg);


typedef struct mw_message_login_ack {
  MW_MESSAGE_STRUCT_HEADER;

  MwLogin login;
  MwPrivacy privacy;
  MwStatus status;

} MwMessageLoginAck;


#define MW_MESSAGE_LOGIN_ACK(msg)		     \
  MW_MESSAGE_TYPE_CAST((msg), (MwMessageLoginAck *), \
		       MW_MESSAGE_TYPE_LOGIN_ACK)


MwMessageLoginAck *mw_message_login_ack_new();


void mw_message_login_ack_free(MwMessageLoginAck *msg);


void mw_message_login_ack_read(MwReadBuffer *mwrb,
			       MwMessageLoginAck *msg,
			       GError **err);


void mw_message_login_ack_write(MwWriteBuffer *mwwb,
				MwMessageLoginAck *msg);


typedef struct mw_message_channel_create {
  MW_MESSAGE_STRUCT_HEADER;

  guint32 reserved;
  guint32 channel;
  MwIdentity target;
  guint32 service;
  guint32 protocol_type;
  guint32 protocol_version;
  guint32 options;
  MwOpaque addtl;
  gboolean creator_flag;
  MwLogin creator;

  guint16 encrypt_prefer;
  guint32 encrypt_count;
  MwEncryptItem *encrypt_items;
  guint16 encrypt_minimum;
  gboolean encrypt_flag;

} MwMessageChannelCreate;


#define MW_MESSAGE_CHANNEL_CREATE(msg)				\
  MW_MESSAGE_TYPE_CAST((msg), (MwMessageChannelCreate *),	\
		       MW_MESSAGE_TYPE_CHANNEL_CREATE)


MwMessageChannelCreate *mw_message_channel_create_new();


void mw_message_channel_create_free(MwMessageChannelCreate *msg);


void mw_message_channel_create_read(MwReadBuffer *mwrb,
				    MwMessageChannelCreate *msg,
				    GError **err);


void mw_message_channel_create_write(MwWriteBuffer *mwwb,
				     MwMessageChannelCreate *msg);


typedef struct mw_message_channel_close {
  MW_MESSAGE_STRUCT_HEADER;

  guint32 reason_code;
  MwOpaque data;
  
} MwMessageChannelClose;


#define MW_MESSAGE_CHANNEL_CLOSE(msg)				\
  MW_MESSAGE_TYPE_CAST((msg), (MwMessageChannelClose *),	\
		       MW_MESSAGE_TYPE_CHANNEL_CLOSE)


MwMessageChannelClose *mw_message_channel_close_new();


void mw_message_channel_close_free(MwMessageChannelClose *msg);


void mw_message_channel_close_read(MwReadBuffer *mwrb,
				   MwMessageChannelClose *msg,
				   GError **err);


void mw_message_channel_close_write(MwWriteBuffer *mwwb,
				    MwMessageChannelClose *msg);


typedef struct mw_message_channel_send {
  MW_MESSAGE_STRUCT_HEADER;

  guint16 type;
  MwOpaque data;
  
} MwMessageChannelSend;


#define MW_MESSAGE_CHANNEL_SEND(msg)			\
  MW_MESSAGE_TYPE_CAST((msg), (MwMessageChannelSend *),	\
		       MW_MESSAGE_TYPE_CHANNEL_SEND)


MwMessageChannelSend *mw_message_channel_send_new();


void mw_message_channel_send_free(MwMessageChannelSend *msg);


void mw_message_channel_send_read(MwReadBuffer *mwrb,
				  MwMessageChannelSend *msg,
				  GError **err);


void mw_message_channel_send_write(MwWriteBuffer *mwwb,
				   MwMessageChannelSend *msg);


typedef struct mw_message_channel_accept {
  MW_MESSAGE_STRUCT_HEADER;

  guint32 service;
  guint32 protocol_type;
  guint32 protocol_version;
  MwOpaque addtl;
  gboolean acceptor_flag;
  MwLogin acceptor;

  guint16 encrypt_mode;
  MwEncryptItem encrypt_item;
  guint16 encrypt_minimum;
  gboolean encrypt_flag;

} MwMessageChannelAccept;


#define MW_MESSAGE_CHANNEL_ACCEPT(msg)				\
  MW_MESSAGE_TYPE_CAST((msg), (MwMessageChannelAccept *),	\
		       MW_MESSAGE_TYPE_CHANNEL_ACCEPT)


MwMessageChannelAccept *mw_message_channel_accept_new();


void mw_message_channel_accept_free(MwMessageChannelAccept *msg);


void mw_message_channel_accept_read(MwReadBuffer *mwrb,
				    MwMessageChannelAccept *msg,
				    GError **err);


void mw_message_channel_accept_write(MwWriteBuffer *mwwb,
				     MwMessageChannelAccept *msg);


typedef struct mw_message_single {
  MW_MESSAGE_STRUCT_HEADER;

  guint32 id;
  MwIdentity target;
  guint32 service;
  guint32 protocol_type;
  guint32 protocol_version;
  
  guint16 type;
  MwOpaque data;

  gboolean sender_flag;
  MwLogin sender;
  
} MwMessageSingle;


#define MW_MESSAGE_SINGLE(msg)				\
  MW_MESSAGE_TYPE_CAST((msg), (MwMessageSingle *),	\
		       MW_MESSAGE_TYPE_SINGLE)


MwMessageSingle *mw_message_single_new();


void mw_message_single_free(MwMessageSingle *msg);


void mw_message_single_read(MwReadBuffer *mwrb,
			    MwMessageSingle *msg,
			    GError **err);


void mw_message_single_write(MwWriteBuffer *mwwb,
			     MwMessageSingle *msg);


typedef struct mw_message_status {
  MW_MESSAGE_STRUCT_HEADER;

  MwStatus status;

} MwMessageStatus;


#define MW_MESSAGE_STATUS(msg)				\
  MW_MESSAGE_TYPE_CAST((msg), (MwMessageStatus *),	\
		       MW_MESSAGE_TYPE_STATUS)


MwMessageStatus *mw_message_status_new();


void mw_message_status_free(MwMessageStatus *msg);


void mw_message_status_read(MwReadBuffer *mwrb,
			    MwMessageStatus *msg,
			    GError **err);


void mw_message_status_write(MwWriteBuffer *mwwb,
			     MwMessageStatus *msg);


typedef struct mw_message_privacy {
  MW_MESSAGE_STRUCT_HEADER;

  MwPrivacy privacy;

} MwMessagePrivacy;


#define MW_MESSAGE_PRIVACY(msg)				\
  MW_MESSAGE_TYPE_CAST((msg), (MwMessagePrivacy *),	\
		       MW_MESSAGE_TYPE_PRIVACY)


MwMessagePrivacy *mw_message_privacy_new();


void mw_message_privacy_free(MwMessagePrivacy *msg);


void mw_message_privacy_read(MwReadBuffer *mwrb,
			     MwMessagePrivacy *msg,
			     GError **err);


void mw_message_privacy_write(MwWriteBuffer *mwwb,
			      MwMessagePrivacy *msg);


typedef struct mw_message_check_service {
  MW_MESSAGE_STRUCT_HEADER;

  guint32 service;

} MwMessageCheckService;


#define MW_MESSAGE_CHECK_SERVICE(msg)				\
  MW_MESSAGE_TYPE_CAST((msg), (MwMessageCheckService *),	\
		       MW_MESSAGE_TYPE_CHECK_SERVICE)


MwMessageCheckService *mw_message_check_service_new();


void mw_message_check_service_free(MwMessageCheckService *msg);


void mw_message_check_service_read(MwReadBuffer *mwrb,
				   MwMessageCheckService *msg,
				   GError **err);


void mw_message_check_service_write(MwWriteBuffer *mwwb,
				    MwMessageCheckService *msg);


typedef struct mw_message_admin_announce {
  MW_MESSAGE_STRUCT_HEADER;

  gchar *text;

} MwMessageAdminAnnounce;


#define MW_MESSAGE_ADMIN_ANNOUNCE(msg)				\
  MW_MESSAGE_TYPE_CAST((msg), (MwMessageAdminAnnounce *),	\
		       MW_MESSAGE_TYPE_ADMIN_ANNOUNCE)


MwMessageAdminAnnounce *mw_message_admin_announce_new();


void mw_message_admin_announce_free(MwMessageAdminAnnounce *msg);


void mw_message_admin_announce_read(MwReadBuffer *mwrb,
				    MwMessageAdminAnnounce *msg,
				    GError **err);


void mw_message_admin_announce_write(MwWriteBuffer *mwwb,
				     MwMessageAdminAnnounce *msg);


typedef struct mw_message_user_announce {
  MW_MESSAGE_STRUCT_HEADER;

  gboolean sender_flag;
  MwLogin sender;

  guint16 unknown_a;
  gboolean may_reply;
  gchar *text;

  guint32 recipient_count;
  gchar **recipients;

} MwMessageUserAnnounce;


#define MW_MESSAGE_USER_ANNOUNCE(msg)				\
  MW_MESSAGE_TYPE_CAST((msg), (MwMessageUserAnnounce *),	\
		       MW_MESSAGE_TYPE_USER_ANNOUNCE)


MwMessageUserAnnounce *mw_message_user_announce_new();


void mw_message_user_announce_free(MwMessageUserAnnounce *msg);


void mw_message_user_announce_read(MwReadBuffer *mwrb,
				   MwMessageUserAnnounce *msg,
				   GError **err);


void mw_message_user_announce_write(MwWriteBuffer *mwwb,
				    MwMessageUserAnnounce *msg);


G_END_DECLS


#endif /* MW_MESSAGE_H */
